#include "kfifo.h"
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>

typedef struct test_t
{
    struct kfifo* fifo;
    int test_num;
}test_t;

long long int test_sum = 0;

static void* KfifoWriter(void *arg)
{
    test_t *pTest_t=(test_t*)arg;
    struct kfifo* fifo  = pTest_t->fifo;
    int test_num = pTest_t->test_num;
    unsigned int ret = 0; 
    for (int i = 0; i < test_num; ++i) {
        ret = __kfifo_put(fifo, (unsigned char*)&i, sizeof(i));
        while (ret == 0) {
            ret = __kfifo_put(fifo, (unsigned char*)&i, sizeof(i));
            // printf("lock-free kfifo is full \n");
        }
        if(ret != sizeof(i)){
            printf("KfifoWriter : ret = %d \n", ret);
        }
        // printf("write : i =%d \n", i);
    }
}

static void* KfifoReader(void *arg)
{
    test_t *pTest_t=(test_t*)arg;
    struct kfifo* fifo  = pTest_t->fifo;
    int test_num = pTest_t->test_num;
    int num = 0;
    unsigned int ret = 0;
    for (int i = 0; i < test_num; ++i) {
        ret = __kfifo_get(fifo, (unsigned char*)&num, sizeof(num));
        while (ret == 0) {
            ret = __kfifo_get(fifo, (unsigned char*)&num, sizeof(num));
            // printf("lock-free kfifo is empty \n");
        }
        if(ret != sizeof(num)){
            printf("KfifoReader : ret = %d , num = %d\n", ret,num);
        }
        if(num != i){
            printf("KfifoReader : ret = %d , num = %d, i = %d, test_sum = %lld \n", ret, num, i, test_sum);
        }
        // printf("read : num = %d \n", num);
        test_sum = test_sum + num;
    }
}

static void* KfifoLockWriter(void *arg)
{
    test_t *pTest_t=(test_t*)arg;
    struct kfifo* fifo  = pTest_t->fifo;
    int test_num = pTest_t->test_num;
    for (int i = 0; i < test_num; ++i) {
        while (kfifo_put(fifo, (unsigned char*)&i, sizeof(i)) == 0) {
            // printf("lock kfifo is full \n");
        }
    }
}

static void* KfifoLockReader(void *arg)
{
    test_t *pTest_t=(test_t*)arg;
    struct kfifo* fifo  = pTest_t->fifo;
    int test_num = pTest_t->test_num;
    int num = 0;
    for (int i = 0; i < test_num; ++i) {
        while (kfifo_get(fifo, (unsigned char*)&num, sizeof(num)) == 0) {
            // printf("lock kfifo is empty \n");
        }
        test_sum = test_sum + num;
    }
}

int main(int argc, char** argv)
{
    int test_num = 100000;
    if (argc > 1) {
        test_num = atoi(argv[1]);
    }

    int test_type = 0;
    if (argc > 2) {
        test_type = atoi(argv[2]);
    }

    printf("test_num = %d ,test_type = %d , test_sum = %lld\n", test_num, test_type, test_sum);

    int idx = 0;
    for(idx=0;idx<10000;idx++){
        test_sum = 0;
        if (test_type == 0) {
            pthread_mutex_t lock;
            pthread_mutex_init(&lock, NULL);

            pthread_t mThreadID_reader;
            pthread_t mThreadID_writer;

            struct kfifo* test_fifo = kfifo_alloc(2048 * sizeof(int), 0, &lock);

            test_t *pTest_t=(test_t*)malloc(sizeof(test_t));
            pTest_t->fifo = test_fifo;
            pTest_t->test_num = test_num;

            struct timeval starttime,endtime;
            gettimeofday(&starttime,0);
            {
                pthread_create(&(mThreadID_writer),NULL,KfifoLockWriter,(void*)pTest_t);
                pthread_create(&(mThreadID_reader),NULL,KfifoLockReader,(void*)pTest_t);
            }

            pthread_join(mThreadID_reader,NULL);
            pthread_join(mThreadID_writer,NULL);
            kfifo_free(test_fifo);
            pthread_mutex_destroy(&lock);

            gettimeofday(&endtime,0);
            double timeuse = 1000000*(endtime.tv_sec - starttime.tv_sec) + endtime.tv_usec - starttime.tv_usec;
            timeuse /=1000;
            printf("lock operation timeuse = %f ms , test_sum = %lld\n", timeuse, test_sum);
        } else if(test_type == 1){
            pthread_mutex_t lock;
            pthread_mutex_init(&lock, NULL);

            pthread_t mThreadID_reader;
            pthread_t mThreadID_writer;

            struct kfifo* test_fifo = kfifo_alloc(2048 * sizeof(int), 0, &lock);
            test_t *pTest_t=(test_t*)malloc(sizeof(test_t));
            pTest_t->fifo = test_fifo;
            pTest_t->test_num = test_num;

            struct timeval starttime,endtime;
            gettimeofday(&starttime,0);
            {
                pthread_create(&(mThreadID_writer),NULL,KfifoWriter,(void*)pTest_t);
                pthread_create(&(mThreadID_reader),NULL,KfifoReader,(void*)pTest_t);
            }

            pthread_join(mThreadID_reader,NULL);
            pthread_join(mThreadID_writer,NULL);

            kfifo_free(test_fifo);
            pthread_mutex_destroy(&lock);

            gettimeofday(&endtime,0);
            double timeuse = 1000000*(endtime.tv_sec - starttime.tv_sec) + endtime.tv_usec - starttime.tv_usec;
            timeuse /=1000;
            printf("lock-free operation timeuse = %f ms , test_sum = %lld\n", timeuse, test_sum);

        }

        long long int temp_sum = 0;
        for (int i = 0; i < test_num; ++i) {
            temp_sum = temp_sum + i;
        }

        printf("temp_sum = %lld -- idx = %d \n", temp_sum, idx);

        if(test_sum != temp_sum){
            printf("error test_sum !!!!!!!!!!!!!!!!!!!!!!!--\n");
            break;
        }

    }

    return 0;
}
